package org.mspring.platform.counter;

import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.mspring.platform.cache.lru.LruCache;
import org.mspring.platform.counter.entity.Count;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

/**
 *
 * @since 2014年4月25日
 * @author Gao Youbo
 */
@Lazy(true)
public class CounterManager implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(CounterManager.class);

    private final long maxSleepTime = 1000 * 60 * 60 * 3; // 数据最大空闲时间：3小时
    private long syncInterval = 5000L; // 同步时间间隔,默认1秒
    private SyncThread syncThread; // 同步线程
    private int currentMapDay; // map中当前保存的数据是哪一天的
    // private final ConcurrentMap<CountKey, CountValue> allMap = new
    // ConcurrentHashMap<>();
    private final ConcurrentMap<CountKey, CountValue> dayMap = new ConcurrentHashMap<>();
    private final LruCache<CountKey, CountValue> all = new LruCache<>(7200);

    @Autowired
    private CountService countService;

    public void setSyncInterval(long syncInterval) {
        this.syncInterval = syncInterval;
    }

    public void increment(String... keys) {
        increment(1L, keys);
    }

    public void increment(long delta, String... keys) {
        if (keys == null || keys.length == 0 || keys.length > 5) {
            log.warn("key不符合规范");
            return;
        }
        CountKey countKey = CountKey.getInstance(keys);
        CountValue countValue = getValue(countKey);
        countValue.increment(delta);
    }

    public long getCount(String... keys) {
        CountKey countKey = CountKey.getInstance(keys);
        CountValue countValue = getValue(countKey);
        if (countValue != null) {
            return countValue.getCount();
        }
        Count count = countService.findByCountKey(countKey.getCountKey());
        if (count == null) {
            return 0L;
        }
        countValue = new CountValue();
        countValue.setCount(count.getCount());
        countValue.setNeedSync(false);
        dayMap.put(countKey, countValue);
        return count.getCount();
    }

    public long getTotalCount(String... keys) {
        CountKey key = CountKey.getInstance(keys);
        CountValue value = all.get(key);
        if (value != null) {
            return value.getCount();
        }
        long count = countService.sumByCountKey(key.getCountKey());
        value = new CountValue();
        value.increment(count);
        all.put(key, value);
        return value.getCount();
    }

    private CountValue getValue(CountKey countKey) {
        CountValue value = dayMap.get(countKey.toString());
        if (value == null) {
            CountValue temp = new CountValue();
            value = dayMap.putIfAbsent(countKey, temp);
            if (value == null) {
                value = temp;
            }
        }
        return value;
    }

    @Override
    public void run() {
        doTask();
    }

    @PostConstruct
    protected void doInit() {
        initMap();
        syncThread = new SyncThread(syncInterval, this);
        syncThread.setDaemon(true);
        syncThread.setName("计数同步线程");
        syncThread.start();
    }

    @PreDestroy
    protected void doDestroy() {
        if (syncThread != null) {
            syncThread.destroy();
            syncThread = null;
        }
        doTask();
    }

    private void doTask() {
        int today = Integer.valueOf(DateFormatUtils.format(new Date(), "yyyyMMdd"));
        if (today != currentMapDay) { // 如果天变了，需要转换一下数据
            currentMapDay = today;
            for (Entry<CountKey, CountValue> entry : dayMap.entrySet()) {
                int dataDay = Integer.valueOf(DateFormatUtils.format(entry.getValue().getLastUpdateTime(), "yyyyMMdd"));
                if (dataDay != today) { // 如果不是当天的数据，就从map中移除
                    syncToDb(dataDay, entry.getKey(), entry.getValue()); // 先持久化
                    dayMap.remove(entry.getKey()); // 再从map中移除
                }
            }
        }
        for (Entry<CountKey, CountValue> entry : dayMap.entrySet()) {
            int day = Integer.valueOf(DateFormatUtils.format(entry.getValue().getLastUpdateTime(), "yyyyMMdd"));
            syncToDb(day, entry.getKey(), entry.getValue());
            if (System.currentTimeMillis() - entry.getValue().getLastVisitTime().getTime() > maxSleepTime) {
                dayMap.remove(entry.getKey());
            }
        }
    }

    public void syncToDb(int day, CountKey key, CountValue value) {
        if (value.isNeedSync()) {
            Count count = new Count();
            count.setKey1(key.getKey1());
            count.setKey2(key.getKey2());
            count.setKey3(key.getKey3());
            count.setKey4(key.getKey4());
            count.setKey5(key.getKey5());
            count.setCountKey(key.getCountKey());
            count.setDay(day);
            count.setCount(value.getCount());
            countService.insertOrUpdate(count);
            value.setNeedSync(false);
        }
    }

    private void initMap() {
        currentMapDay = Integer.valueOf(DateFormatUtils.format(new Date(), "yyyyMMdd"));
        List<Count> counts = countService.listByDay(currentMapDay);
        if (counts == null || counts.isEmpty()) {
            return;
        }
        for (Count count : counts) {
            CountKey key = CountKey.getInstance(count);
            CountValue value = new CountValue();
            value.setCount(count.getCount());
            value.setLastUpdateTime(count.getUpdateTime());
            value.setNeedSync(false);
            dayMap.put(key, value);
        }

    }
}
